package me.zhouzhuo810.magpiex.utils;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BlendMode;
import android.graphics.BlendModeColorFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.RequiresApi;
import androidx.appcompat.content.res.AppCompatResources;

/**
 * Drawable相关工具类
 *
 * @author zhouzhuo810
 */
public class DrawableUtil {
    
    private static final String TAG = DrawableUtil.class.getSimpleName();
    
    //节省每次创建时产生的开销，但要注意多线程操作synchronized
    private static final Canvas sCanvas = new Canvas();
    
    /**
     * 代码生成CheckBox的selector。
     *
     * @param idNormal     没选中的图片
     * @param idChecked    选中的图片
     * @param colorChecked 选中的颜色
     * @return StateListDrawable
     */
    public static StateListDrawable newCheckSelector(@DrawableRes int idNormal, @DrawableRes int idChecked, @ColorInt int colorChecked) {
        StateListDrawable bg = new StateListDrawable();
        Drawable normal = idNormal == -1 ? null : SimpleUtil.getDrawable(idNormal);
        Drawable checked = idChecked == -1 ? null : SimpleUtil.getDrawable(idChecked);
        if (checked != null) {
            try {
                setDrawableColor(checked, colorChecked);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        bg.addState(new int[]{android.R.attr.state_checked}, checked);
        bg.addState(new int[]{android.R.attr.state_selected}, checked);
        // View.EMPTY_STATE_SET
        bg.addState(new int[]{}, normal);
        return bg;
    }
    
    /**
     * 设置Drawable的颜色
     *
     * @param drawable Drawable
     * @param color    颜色值
     */
    public static void setDrawableColor(@NonNull Drawable drawable, @ColorInt int color) {
        if (drawable instanceof GradientDrawable) {
            ((GradientDrawable) drawable.mutate()).setColor(color);
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.SRC_ATOP));
            } else {
                drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
            }
        }
    }
    
    /**
     * 圆角矩形背景 Shape
     *
     * @param radius 圆角半径
     * @param color  颜色
     */
    public static GradientDrawable roundShape(int radius, int color) {
        return roundShape(radius, color, 0, 0, 0, 0);
    }
    
    /**
     * 圆角矩形背景 Shape
     *
     * @param radius      圆角半径
     * @param color       填充颜色
     * @param strokeColor 边框颜色
     */
    public static GradientDrawable roundShape(int radius, int color, int strokeColor) {
        return roundShape(radius, color, strokeColor, 2, 0, 0);
    }
    
    /**
     * 圆角矩形背景 Shape
     *
     * @param radius      圆角半径
     * @param color       填充颜色
     * @param strokeColor 边框颜色
     * @param strokeWidth 边框宽度
     */
    
    public static GradientDrawable roundShape(int radius, int color, int strokeColor, int strokeWidth) {
        return roundShape(radius, color, strokeColor, strokeWidth, 0, 0);
    }
    
    /**
     * 圆角矩形背景 Shape
     *
     * @param radius      圆角半径
     * @param color       填充颜色
     * @param strokeWidth 边框宽度
     * @param strokeColor 边框颜色
     * @param dashWidth   边框虚线长度
     * @param dashGap     边框虚线空隙
     */
    public static GradientDrawable roundShape(int radius, int color, int strokeColor, int strokeWidth, int dashWidth, int dashGap) {
        return new ShapeBuilder()
            .shape(GradientDrawable.RECTANGLE)
            .radius(radius)
            .color(color)
            .strokeColor(strokeColor)
            .strokeWidth(strokeWidth)
            .strokeDashWidth(dashWidth)
            .strokeDashGap(dashGap)
            .build();
    }
    
    /**
     * 渐变背景
     *
     * @param radius    圆角
     * @param fromColor 渐变开始颜色
     * @param toColor   渐变结束颜色
     */
    public static GradientDrawable gradientShape(int radius, int fromColor, int toColor) {
        return gradientShape(radius, fromColor, toColor, GradientDrawable.Orientation.LEFT_RIGHT, 0, 0, 0, 0);
    }
    
    /**
     * 渐变背景
     *
     * @param radius      圆角半径
     * @param fromColor   渐变开始颜色
     * @param toColor     渐变结束颜色
     * @param orientation 渐变方向
     */
    public static GradientDrawable gradientShape(int radius, int fromColor, int toColor, GradientDrawable.Orientation orientation) {
        return gradientShape(radius, fromColor, toColor, orientation, 0, 0, 0, 0);
    }
    
    /**
     * 渐变背景
     *
     * @param radius      圆角半径
     * @param fromColor   渐变开始颜色
     * @param toColor     渐变结束颜色
     * @param orientation 渐变方向
     * @param strokeColor 边框颜色
     * @param strokeWidth 边框宽度
     * @param dashWidth   虚线长度
     * @param dashGap     虚线间隙
     */
    public static GradientDrawable gradientShape(int radius, int fromColor, int toColor, GradientDrawable.Orientation orientation, int strokeColor, int strokeWidth, int dashWidth, int dashGap) {
        return new ShapeBuilder()
            .shape(GradientDrawable.RECTANGLE)
            .radius(radius)
            .gradient(true)
            .fromColor(fromColor)
            .toColor(toColor)
            .gradientOrientation(orientation)
            .strokeColor(strokeColor)
            .strokeWidth(strokeWidth)
            .strokeDashWidth(dashWidth)
            .strokeDashGap(dashGap)
            .build();
    }
    
    public static class ShapeBuilder {
        
        private int fromColor;
        private int toColor;
        private boolean gradient;
        private GradientDrawable.Orientation gradientOrientation = GradientDrawable.Orientation.LEFT_RIGHT;
        private int gradientType;
        private float topLeftRadius;
        private float topRightRadius;
        private float bottomLeftRadius;
        private float bottomRightRadius;
        private int strokeWidth;
        private int strokeColor;
        private int strokeDashWidth;
        private int strokeDashGap;
        private int shape = GradientDrawable.RECTANGLE;
        private int color;
        private int ringThickness;
        private int alpha = 0xFF;
        
        public ShapeBuilder() {
        }
        
        /**
         * 设置 shape
         * <br>
         * {@link GradientDrawable#RECTANGLE}
         * <br>
         * {@link GradientDrawable#OVAL}
         * <br>
         * {@link GradientDrawable#LINE}
         * <br>
         * {@link GradientDrawable#RING}
         */
        public ShapeBuilder shape(int shape) {
            this.shape = shape;
            return this;
        }
        
        /**
         * 填充颜色
         *
         * @param color 颜色值
         */
        public ShapeBuilder color(@ColorInt int color) {
            this.color = color;
            return this;
        }
        
        /**
         * 边框颜色
         *
         * @param strokeColor 颜色值
         */
        public ShapeBuilder strokeColor(@ColorInt int strokeColor) {
            this.strokeColor = strokeColor;
            return this;
        }
        
        /**
         * 边框宽度
         *
         * @param strokeWidth 宽度
         */
        public ShapeBuilder strokeWidth(@Px int strokeWidth) {
            this.strokeWidth = strokeWidth;
            return this;
        }
        
        /**
         * 虚线边框的线宽
         *
         * @param strokeDashWidth 宽度
         */
        public ShapeBuilder strokeDashWidth(@Px int strokeDashWidth) {
            this.strokeDashWidth = strokeDashWidth;
            return this;
        }
        
        /**
         * 虚线边框的空隙宽度
         *
         * @param strokeDashGap 宽度
         */
        public ShapeBuilder strokeDashGap(@Px int strokeDashGap) {
            this.strokeDashGap = strokeDashGap;
            return this;
        }
        
        /**
         * 左上角圆角半径
         *
         * @param topLeftRadius 半径
         */
        public ShapeBuilder topLeftRadius(float topLeftRadius) {
            this.topLeftRadius = topLeftRadius;
            return this;
        }
        
        /**
         * 右上角圆角半径
         *
         * @param topRightRadius 半径
         */
        public ShapeBuilder topRightRadius(float topRightRadius) {
            this.topRightRadius = topRightRadius;
            return this;
        }
        
        /**
         * 左下角圆角半径
         *
         * @param bottomLeftRadius 半径
         */
        public ShapeBuilder bottomLeftRadius(float bottomLeftRadius) {
            this.bottomLeftRadius = bottomLeftRadius;
            return this;
        }
        
        /**
         * 右下角圆角半径
         *
         * @param bottomRightRadius 半径
         */
        public ShapeBuilder bottomRightRadius(float bottomRightRadius) {
            this.bottomRightRadius = bottomRightRadius;
            return this;
        }
        
        /**
         * 半径
         *
         * @param radius 半径
         */
        public ShapeBuilder radius(float radius) {
            this.topLeftRadius = radius;
            this.topRightRadius = radius;
            this.bottomLeftRadius = radius;
            this.bottomRightRadius = radius;
            return this;
        }
        
        
        /**
         * 透明度
         *
         * @param alpha 透明度，0-255
         */
        public ShapeBuilder alpha(int alpha) {
            this.alpha = alpha;
            return this;
        }
        
        
        /**
         * 是否渐变
         *
         * @param gradient 是否
         */
        public ShapeBuilder gradient(boolean gradient) {
            this.gradient = gradient;
            return this;
        }
        
        /**
         * 渐变开始颜色
         *
         * @param fromColor 颜色值
         */
        public ShapeBuilder fromColor(@ColorInt int fromColor) {
            this.fromColor = fromColor;
            return this;
        }
        
        /**
         * 渐变结束颜色
         *
         * @param toColor 颜色值
         */
        public ShapeBuilder toColor(@ColorInt int toColor) {
            this.toColor = toColor;
            return this;
        }
        
        /**
         * 圆环的厚度
         *
         * @param ringThickness 厚度
         */
        @RequiresApi(Build.VERSION_CODES.Q)
        public ShapeBuilder ringThickness(@Px int ringThickness) {
            this.ringThickness = ringThickness;
            return this;
        }
        
        /**
         * 渐变类型
         * <br>
         * {@link GradientDrawable#LINEAR_GRADIENT}
         * <br>
         * {@link GradientDrawable#RADIAL_GRADIENT}
         * <br>
         * {@link GradientDrawable#SWEEP_GRADIENT}
         *
         * @param gradientType 类型
         */
        public ShapeBuilder gradientType(int gradientType) {
            this.gradientType = gradientType;
            return this;
        }
        
        /**
         * 渐变方向
         *
         * @param gradientOrientation 方向
         */
        public ShapeBuilder gradientOrientation(GradientDrawable.Orientation gradientOrientation) {
            this.gradientOrientation = gradientOrientation;
            return this;
        }
        
        /**
         * 生成 GradientDrawable
         *
         * @return GradientDrawable
         */
        public GradientDrawable build() {
            GradientDrawable drawable = new GradientDrawable();
            drawable.setColor(color);
            drawable.setShape(shape);
            drawable.setCornerRadii(new float[]{topLeftRadius, topLeftRadius, topRightRadius, topRightRadius,
                bottomRightRadius, bottomRightRadius, bottomLeftRadius, bottomLeftRadius});
            if (strokeWidth > 0) {
                drawable.setStroke(strokeWidth, strokeColor, strokeDashWidth, strokeDashGap);
            }
            drawable.setOrientation(gradientOrientation == null ? GradientDrawable.Orientation.LEFT_RIGHT : gradientOrientation);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                drawable.setThickness(ringThickness);
            }
            drawable.setGradientType(gradientType);
            drawable.setAlpha(alpha);
            if (gradient) {
                drawable.setColors(new int[]{fromColor, toColor});
            }
            drawable.setDither(true);
            return drawable;
        }
        
        /**
         * 应用到控件
         *
         * @param view View
         */
        public void applyTo(View view) {
            if (view != null) {
                view.setBackground(build());
            }
        }
    }
    
    
    /**
     * 从一个view创建Bitmap。
     * 注意点：绘制之前要清掉 View 的焦点，因为焦点可能会改变一个 View 的 UI 状态。
     * 来源：https://github.com/tyrantgit/ExplosionField
     *
     * @param view  传入一个 View，会获取这个 View 的内容创建 Bitmap。
     * @param scale 缩放比例，对创建的 Bitmap 进行缩放，数值支持从 0 到 1。
     */
    public static Bitmap createBitmapFromView(View view, float scale) {
        if (view instanceof ImageView) {
            Drawable drawable = ((ImageView) view).getDrawable();
            if (drawable != null && drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            }
        }
        view.clearFocus();
        Bitmap bitmap = createBitmapSafely((int) (view.getWidth() * scale),
            (int) (view.getHeight() * scale), Bitmap.Config.ARGB_8888, 1);
        if (bitmap != null) {
            synchronized (sCanvas) {
                Canvas canvas = sCanvas;
                canvas.setBitmap(bitmap);
                canvas.save();
                canvas.drawColor(Color.WHITE); // 防止 View 上面有些区域空白导致最终 Bitmap 上有些区域变黑
                canvas.scale(scale, scale);
                view.draw(canvas);
                canvas.restore();
                canvas.setBitmap(null);
            }
        }
        return bitmap;
    }
    
    public static Bitmap createBitmapFromView(View view) {
        return createBitmapFromView(view, 1f);
    }
    
    /**
     * 从一个view创建Bitmap。把view的区域截掉leftCrop/topCrop/rightCrop/bottomCrop
     */
    public static Bitmap createBitmapFromView(View view, int leftCrop, int topCrop, int rightCrop, int bottomCrop) {
        Bitmap originBitmap = createBitmapFromView(view);
        if (originBitmap == null) {
            return null;
        }
        Bitmap cutBitmap = createBitmapSafely(view.getWidth() - rightCrop - leftCrop, view.getHeight() - topCrop - bottomCrop, Bitmap.Config.ARGB_8888, 1);
        if (cutBitmap == null) {
            return null;
        }
        Canvas canvas = new Canvas(cutBitmap);
        Rect src = new Rect(leftCrop, topCrop, view.getWidth() - rightCrop, view.getHeight() - bottomCrop);
        Rect dest = new Rect(0, 0, view.getWidth() - rightCrop - leftCrop, view.getHeight() - topCrop - bottomCrop);
        canvas.drawColor(Color.WHITE); // 防止 View 上面有些区域空白导致最终 Bitmap 上有些区域变黑
        canvas.drawBitmap(originBitmap, src, dest, null);
        originBitmap.recycle();
        return cutBitmap;
    }
    
    /**
     * 安全的创建bitmap。
     * 如果新建 Bitmap 时产生了 OOM，可以主动进行一次 GC - System.gc()，然后再次尝试创建。
     *
     * @param width      Bitmap 宽度。
     * @param height     Bitmap 高度。
     * @param config     传入一个 Bitmap.Config。
     * @param retryCount 创建 Bitmap 时产生 OOM 后，主动重试的次数。
     * @return 返回创建的 Bitmap。
     */
    public static Bitmap createBitmapSafely(int width, int height, Bitmap.Config config, int retryCount) {
        try {
            return Bitmap.createBitmap(width, height, config);
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            if (retryCount > 0) {
                System.gc();
                return createBitmapSafely(width, height, config, retryCount - 1);
            }
            return null;
        }
    }
    
    /**
     * 创建一张指定大小的纯色图片，支持圆角
     *
     * @param resources    Resources对象，用于创建BitmapDrawable
     * @param width        图片的宽度
     * @param height       图片的高度
     * @param cornerRadius 图片的圆角，不需要则传0
     * @param filledColor  图片的填充色
     * @return 指定大小的纯色图片
     */
    public static BitmapDrawable createDrawableWithSize(Resources resources, int width, int height, int cornerRadius, @ColorInt int filledColor) {
        Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        
        if (filledColor == 0) {
            filledColor = Color.TRANSPARENT;
        }
        
        if (cornerRadius > 0) {
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(filledColor);
            canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);
        } else {
            canvas.drawColor(filledColor);
        }
        return new BitmapDrawable(resources, output);
    }
    
    /**
     * 设置Drawable的颜色
     * <b>这里不对Drawable进行mutate()，会影响到所有用到这个Drawable的地方，如果要避免，请先自行mutate()</b>
     */
    public static ColorFilter setDrawableTintColor(Drawable drawable, @ColorInt int tintColor) {
        LightingColorFilter colorFilter = new LightingColorFilter(Color.argb(255, 0, 0, 0), tintColor);
        if (drawable != null) {
            drawable.setColorFilter(colorFilter);
        }
        return colorFilter;
    }
    
    /**
     * 由一个drawable生成bitmap
     */
    public static Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable == null)
            return null;
        else if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }
        
        int intrinsicWidth = drawable.getIntrinsicWidth();
        int intrinsicHeight = drawable.getIntrinsicHeight();
        
        if (!(intrinsicWidth > 0 && intrinsicHeight > 0))
            return null;
        
        try {
            Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
            Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, config);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 创建一张渐变图片，支持韵脚。
     *
     * @param startColor 渐变开始色
     * @param endColor   渐变结束色
     * @param radius     圆角大小
     * @param centerX    渐变中心点 X 轴坐标
     * @param centerY    渐变中心点 Y 轴坐标
     * @return 返回所创建的渐变图片。
     */
    @TargetApi(16)
    public static GradientDrawable createCircleGradientDrawable(@ColorInt int startColor,
                                                                @ColorInt int endColor, int radius,
                                                                @FloatRange(from = 0f, to = 1f) float centerX,
                                                                @FloatRange(from = 0f, to = 1f) float centerY) {
        GradientDrawable gradientDrawable = new GradientDrawable();
        gradientDrawable.setColors(new int[]{
            startColor,
            endColor
        });
        gradientDrawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
        gradientDrawable.setGradientRadius(radius);
        gradientDrawable.setGradientCenter(centerX, centerY);
        return gradientDrawable;
    }
    
    
    /**
     * 动态创建带上分隔线或下分隔线的Drawable。
     *
     * @param separatorColor 分割线颜色。
     * @param bgColor        Drawable 的背景色。
     * @param top            true 则分割线为上分割线，false 则为下分割线。
     * @return 返回所创建的 Drawable。
     */
    public static LayerDrawable createItemSeparatorBg(@ColorInt int separatorColor, @ColorInt int bgColor, int separatorHeight, boolean top) {
        
        ShapeDrawable separator = new ShapeDrawable();
        separator.getPaint().setStyle(Paint.Style.FILL);
        separator.getPaint().setColor(separatorColor);
        
        ShapeDrawable bg = new ShapeDrawable();
        bg.getPaint().setStyle(Paint.Style.FILL);
        bg.getPaint().setColor(bgColor);
        
        Drawable[] layers = {separator, bg};
        LayerDrawable layerDrawable = new LayerDrawable(layers);
        
        layerDrawable.setLayerInset(1, 0, top ? separatorHeight : 0, 0, top ? 0 : separatorHeight);
        return layerDrawable;
    }
    
    
    /////////////// VectorDrawable /////////////////////
    
    public static
    @Nullable
    Drawable getVectorDrawable(Context context, @DrawableRes int resVector) {
        try {
            return AppCompatResources.getDrawable(context, resVector);
        } catch (Exception e) {
            Log.d(TAG, "Error in getVectorDrawable. resVector=" + resVector + ", resName=" + context.getResources().getResourceName(resVector) + e.getMessage());
            return null;
        }
    }
    
    public static Bitmap vectorDrawableToBitmap(Context context, @DrawableRes int resVector) {
        Drawable drawable = getVectorDrawable(context, resVector);
        if (drawable != null) {
            Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b);
            drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
            drawable.draw(c);
            return b;
        }
        return null;
    }
}
